#ifndef GAME_ENTITY_H
#define GAME_ENTITY_H
#include "game_platform.h"
#include "game_math.h"
#include "game_world.h"
#include "game_brain.h"

struct move_spec {
    bool32 UnitMaxAccelVector;
    // 表示单位最大加速度的标志位，通常是一个布尔值，决定是否使用最大加速度向量
    real32 Speed;  // 单位的速度，通常表示物体的移动速度，单位可以是米/秒等
    real32 Drag;  // 阻力系数，用来表示空气阻力或其他类型的阻力，通常影响加速度或速度的变化
};

// 定义实体类型的枚举
enum entity_type {
    EntityType_Null,  // 空实体类型，表示没有实体
    // 定义一个空间实体类型，用于切割和定义世界的区域
    // 这个实体类型将承担房间和其他区域的空间切割功能
    // 作为一个复杂的碰撞体积，EntityType_Space 也能够根据需要变形，
    // 比如成为洞穴等结构，允许更多灵活的空间形态
    EntityType_Floor,
    EntityType_FloatyThingForNow,
    EntityType_HeroBody,
    EntityType_HeroHead,
    EntityType_Wall,      // 墙壁实体类型，用于表示墙壁
    EntityType_Familiar,  // 熟悉的实体，通常是跟随玩家的伙伴或宠物
    EntityType_Monster,   // 怪物类型，敌对实体
    EntityType_Stairwell,  // 楼梯间实体类型，用于表示连接上下楼层的特殊区域。
};

// 定义模拟实体的标志枚举类型
enum entity_flags {
    EntityFlag_Collides = (1 << 0),
    EntityFlag_Deleted = (1 << 1),
    EntityFlag_Active = (1 << 2),
};

// 表示实体的单个碰撞体积
// 该结构体定义了一个实体的碰撞体积，包括偏移量和尺寸，用于描述该碰撞体积在空间中的位置和范围。
// 偏移量 (OffsetP) 是相对于实体位置的偏移，Dim 定义了碰撞体积的尺寸。
struct entity_collision_volume {
    v3 OffsetP;  // 偏移量：表示碰撞体积相对于实体的偏移位置（例如，碰撞体积的中心）。
    v3 Dim;      // 尺寸：表示碰撞体积的尺寸，包括宽度、高度和深度。
};
// 定义一个表示模拟实体的结构体（提前申明）
struct entity;
struct entity_traversable_point {
    v3 P;
    entity* Occupier;
};

// 包含多个碰撞体积的组
// 该结构体存储了一个碰撞体积组的所有信息，允许一个实体具有多个碰撞体积。
// VolumeCount 表示该组包含的碰撞体积数量，Volume 是指向这些碰撞体积数组的指针。
struct entity_collision_volume_group {
    // 定义实体的总碰撞体积（TotalVolume）。
    // TotalVolume 是一个简单的边界体积，用于快速判断实体是否与某区域发生重叠。
    // 它能够完全包围实体的所有部分，是区域检测的主要依据。
    // 在区域检测中，仅需要判断 TotalVolume 与目标区域是否重叠，无需深入检测实体的具体形状。
    // 这种设计有助于优化性能，减少复杂形状检测带来的额外开销。
    entity_collision_volume TotalVolume;

    uint32 VolumeCount;  // 碰撞体积数量：该组中包含的碰撞体积的数量。
    entity_collision_volume* Volumes;
};

// 结构体用于表示一个命中点（Hit Point）
struct hit_point {
    uint8 Flags;  // 标记位，表示命中点的状态（例如是否中毒、是否脆弱等）
    uint8 FilledAmount;  // 填充量，表示命中点的当前填充程度（例如生命值的百分比）
};

struct entity_id {
    uint32 Value;
};

struct entity_reference {
    entity* Ptr;  // 指向模拟实体的指针
    entity_id Index;
};
inline bool32 ReferencesAreEqual(entity_reference A, entity_reference B) {
    bool32 Result = (A.Ptr == B.Ptr) &&  //
                    (A.Index.Value == B.Index.Value);
    return Result;
}

enum entity_movement_mode {
    MovementMode_Planted,
    MovementMode_Hopping,
    MovementMode_Sliding,
    MovementMode_AngleOffset,
    MovementMode_AngleAttackSwipe,
};

struct traversable_reference {
    entity_reference Entity;
    uint32 Index;
};
enum entity_visible_piece_movement {
    PieceMove_AxesDeform = 0x1,
    PieceMove_BobOffset = 0x2,
};
struct entity_visible_piece {
    v4 Color;
    assets_type_id AssetType;
    real32 Height;
    v3 Offset;
    uint32 Flags;
};
struct manual_sort_key {
    uint32 AlwaysInFrontOf;
    uint32 AlwaysBehind;
};

struct entity {
    brain_slot BrainSlot;
    brain_id BrainID;
    int32 ZLayer;
    real32 ddtBob;
    manual_sort_key ManualSort;
    uint32 PairedEntityCount;
    entity_reference* PairedEntities;
    v3 P;  // 相对于摄像头的位置

    // dZ: 当前实体在Z轴上的速度（速度矢量的Z分量），
    // 用于表示实体在垂直方向上的速度，通常在跳跃过程中会受到重力加速度的影响。
    real32 dZ;

    // DistanceLimit: 表示实体可以移动或交互的最大允许距离。
    // 该值可以设置为零，表示没有移动限制，或者设置为一个具体值来限制移动的范围。
    real32 DistanceLimit;

    // 指向该实体的碰撞体积组的指针
    // 每个实体可以拥有一个或多个碰撞体积，这个指针指向包含所有相关碰撞体积的集合（如：碰撞体积、尺寸、偏移量等）。
    // 碰撞体积组可用于管理多个碰撞体积，尤其是在实体的碰撞体积类型和大小可能不同的情况下。
    // 这个设计使得同一类型的实体可以共享相同的碰撞体积组，而不需要为每个实体单独存储碰撞体积数据。
    entity_collision_volume_group* Collision;

    entity_id ID;

    // Flags: 一个 32 位的无符号整数，用于存储实体的多个标志位。
    // 每个标志位代表实体的不同状态或行为，例如是否参与碰撞、是否为非空间实体等。
    // 通过按位操作（如按位与、按位或）可以检查或设置特定的标志位。
    uint32 Flags;

    // 最大生命值，表示角色或实体可以拥有的最大生命点数
    uint32 HitPointMax;

    // 定义一个包含16个命中点（Hit Point）的数组
    hit_point HitPoint[16];  // 数组中的每个元素表示一个独立的命中点

    entity_reference Head;

    // WalkableDim: 表示可行走区域的矩形。
    // 这个矩形定义了角色可以在其上自由移动的空间区域。通常用于标识楼梯、平台等可行走区域。
    // 在楼梯的情况下，这个区域不涉及碰撞检测，而是用于描述角色如何在该区域内移动。
    // 该区域可能与实际的碰撞体不同，它只是作为可行走区域的参考，可能会用于导航和移动控制。
    v2 WalkableDim;

    v3 dP;
    v3 ddP;

    real32 FacingDirection;
    // 表示实体的面向方向，通常用于决定实体在游戏中的朝向（例如：朝上、朝下、朝左或朝右）。

    // tBob 用于表示头部上下浮动的时间或周期，类型为 real32（32位浮点数）。
    // 这个变量可以用于控制头部浮动的速率或时间进程。
    // 根据游戏的需要，可以用 tBob 作为周期性波动的输入，来调整头部浮动的效果。
    real32 tBob;
    real32 dtBob;

    // WalkableHeight: 用于表示可行走的高度，通常由区域的最小Z值和楼梯高度组合而成。
    // 该值决定了玩家是否可以跨越特定区域的楼梯或地面，避免不合理的碰撞行为。
    // WalkableHeight 是在不同楼层或高度变化的区域内，确定玩家是否能够顺利行走的关键参数。
    // 它可以根据不同的地形变化动态调整，从而确保碰撞检测与玩家行走行为的准确性。
    real32 WalkableHeight;

    entity_movement_mode MovementMode;
    traversable_reference Occupying;
    traversable_reference CameFrom;
    real32 tMovement;

    v3 AngleBase;
    real32 AngleCurrent;
    real32 AngleStart;
    real32 AngleTarget;
    real32 AngleCurrentDistance;
    real32 AngleBaseDistance;
    real32 AngleSwipDistance;

    v2 XAxis;
    v2 YAxis;
    v2 FloorDisplace;
    world_position ChunkP;
    // 碰撞体积数组：指向一个碰撞体积数组的指针，数组中包含实体的所有碰撞体积。
    uint32 TraversableCount;
    entity_traversable_point Traversables[16];

    uint32 PieceCount;
    entity_visible_piece Pieces[4];
};

// 定义一个结构体，用于表示一个模拟实体的哈希值
struct entity_hash {
    entity* Ptr;      // 指向模拟实体的指针
    entity_id Index;  // 模拟实体的索引
};

#define InValidP \
    v3 { 1000000.0f, 1000000.0f, 1000000.0f }

// IsSet: 检查指定实体是否设置了特定的标志位。
// 参数:
//   - Entity: 需要检查标志位的实体对象指针。
//   - Flag: 要检查的标志位。
// 返回值:
//   - 如果实体的 Flags 中包含指定的标志位，则返回 true (非零)；
//     否则，返回 false (零)。
// 说明: 使用按位与操作检查实体的 Flags 是否包含传入的 Flag 标志位。
inline bool32 IsSet(entity* Entity, uint32 Flag) {
    bool32 Result = Entity->Flags & Flag;  // 检查 Flags 中是否包含指定的标志位
    return Result;                         // 返回检查结果
}

// 设置实体的指定标志位。
// 通过按位或运算将给定的标志值添加到实体的 Flags 字段中，表示该实体具有该标志。
inline void AddFlags(entity* Entity, uint32 Flag) {
    Entity->Flags |= Flag;  // 将 Flag 加入实体的 Flags 标志位
}

// ClearFlag: 清除实体的特定标志
// 此函数通过与给定标志的按位取反结果进行按位与操作，
// 来清除实体的 Flags 字段中的相应位。
// 调用此函数后，指定的标志将从实体的标志状态中移除。
inline void ClearFlag(entity* Entity, uint32 Flag) {
    // 使用按位与操作与标志的取反结果，来清除对应的标志位
    Entity->Flags &= ~Flag;
}

// 获取实体的地面位置
inline v3 GetEntityGroundPoint(entity* Entity) {
    // 计算实体的地面位置：当前位置加上Z轴偏移（实体高度的一半）
    v3 Result = Entity->P;

    // 返回计算得到的地面位置
    return Result;
}

// 获取实体的地面位置
inline v3 GetEntityGroundPoint(entity* Entity, v3 ForEntityP) {
    (void)Entity;
    v3 Result = ForEntityP;

    // 返回计算得到的地面位置
    return Result;
}

inline bool32 IsDeleted(entity* E) {
    bool32 Result = (E->Flags & EntityFlag_Deleted);
    return Result;
}

// GetStairGround: 计算给定实体（楼梯）在指定地面位置的Z轴高度。
// 参数:
//   Entity - 目标实体，必须是类型为楼梯（EntityType_Stairwell）
//   AtGroundPoint - 地面上的一个点，用于计算该点在楼梯上的高度
// 返回值:
//   该点在楼梯上的Z轴高度，表示角色在楼梯上的实际高度。
inline real32 GetStairGround(entity* Entity, v3 AtGroundPoint) {
    // 创建一个矩形区域，表示楼梯的可行走区域，基于楼梯的中心位置和尺寸
    rectangle2 RegionRect = RectCenterDim(Entity->P.xy, Entity->WalkableDim);

    // 计算目标地面点在楼梯矩形区域中的重心坐标，并确保坐标限制在[0, 1]范围内
    v2 Bary = Clamp01(GetBarycentric(RegionRect, AtGroundPoint.xy));

    // 通过重心坐标的Y分量计算该点在楼梯上的Z坐标
    real32 Result = Entity->P.z + Bary.y * Entity->WalkableHeight;

    // 返回计算得到的Z坐标，表示地面点的实际高度
    return Result;
}

struct memory_arena;
struct game_mode_world;
struct GameState;
struct sim_region;
struct game_state;
world_position MapIntoChunkSpace(world* World, world_position BasePos, v3 Offset);
extern world_chunk* GetWorldChunk(world* World, int32 ChunkX, int32 ChunkY,
                                  int32 ChunkZ,  //
                                  memory_arena* Arena);
entity_traversable_point GetSimSpaceTraversable(entity* Entity, uint32 Index);
move_spec DefaultMoveSpec();
void MakeEntitySpecial(entity* Entity, v3 P, v3 dP);
void UpdateAndRenderEntities(game_mode_world* WorldMode, sim_region* SimRegion,
                             render_group* RenderGroup, v3 CameraP, loaded_bitmap* DrawBuffer,
                             v4 BackgroundColor, real32 dt, transient_state* TranState, v2 MouseP);
#endif